﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Collections;
using System.Web;

namespace MKLIB
{
	public class SparrowJson
	{
		private const string SpecialChars = "\"\r\n/";
		private static DateTime DT19700101 = new DateTime(1970, 1, 1);

		private static string ConvertString(string value)
		{
			if (value == null) return "null";
			else if (value.Length < 1) return "";
			else
			{
				string CH;
				foreach (char ch in SpecialChars)
				{
					CH = ch.ToString();
					value = value.Replace(CH, "\\" + CH);
				}
				return String.Format("\"{0}\"", value);
			}
		}
		private static double ConvertDateTime(DateTime dt)
		{
			var sec = ((TimeSpan)(dt.ToUniversalTime() - DT19700101)).TotalMilliseconds;
			return (UInt64)sec;
		}
		private static string ConvertLiteral(object value)
		{
			if (value == null) return "null";

			TypeCode typeCode = Type.GetTypeCode(value.GetType());
			switch (typeCode)
			{
				case TypeCode.Boolean:
					return value.ToString().ToLower();
				case TypeCode.Byte:
				case TypeCode.Decimal:
				case TypeCode.Single:
				case TypeCode.Double:
				case TypeCode.SByte:
				case TypeCode.Int16:
				case TypeCode.Int32:
				case TypeCode.Int64:
				case TypeCode.UInt16:
				case TypeCode.UInt32:
				case TypeCode.UInt64:
					return value.ToString();
				case TypeCode.Char:
					return String.Format("\"{0}\"", value);
				case TypeCode.DBNull:
				case TypeCode.Empty:
						return "null";
				case TypeCode.Object:
					throw new NotImplementedException();
				case TypeCode.DateTime:
					return ConvertDateTime((DateTime)value).ToString();
				case TypeCode.String:
					return ConvertString(value as String);
				default:
					return String.Format("[object {0}]", value.GetType().Name);
			}
		}

		private static string ConvertObject(IDictionary obj)
		{
			StringBuilder builder = new StringBuilder();
			foreach (object key in obj.Keys)
			{
				builder.Append(ConvertLiteral(key));
				builder.Append(":");
				builder.Append(Convert(obj[key]));
				builder.Append(",");
			}
			if (builder.Length > 0) builder.Remove(builder.Length - 1, 1);
			return String.Format("{{{0}}}", builder.ToString());
		}
		private static string ConvertArray(IEnumerable arr)
		{
			StringBuilder builder = new StringBuilder();
			foreach (var a in arr)
			{
				builder.Append(Convert(a));
				builder.Append(",");
			}
			if (builder.Length > 0) builder.Remove(builder.Length - 1, 1);
			return String.Format("[{0}]", builder.ToString());
		}

		private static bool IsLiteral(object value)
		{
			if (value == null) return true;
			Type type = value.GetType();
			TypeCode typeCode = Type.GetTypeCode(type);
			switch (typeCode)
			{
				case TypeCode.Boolean:
				case TypeCode.Byte:
				case TypeCode.Char:
				case TypeCode.DateTime:
				case TypeCode.DBNull:
				case TypeCode.Decimal:
				case TypeCode.Double:
				case TypeCode.Empty:
				case TypeCode.Int16:
				case TypeCode.Int32:
				case TypeCode.Int64:
				// case TypeCode.Object:
				case TypeCode.SByte:
				case TypeCode.Single:
				case TypeCode.String:
				case TypeCode.UInt16:
				case TypeCode.UInt32:
				case TypeCode.UInt64:
					return true;
				default:
					return false;
			}
		}

		private static bool IsEmpty(IEnumerable obj)
		{
			foreach (var o in obj)
			{
				return false;
			}
			return true;
		}

		private static Hashtable ConvertToHashTable(object obj, ArrayList chain)
		{
			var hash = new Hashtable();
			chain.Add(obj);

			var type = obj.GetType();
			var feilds = type.GetFields();
			object val;
			foreach (var feild in feilds)
			{
				val = feild.GetValue(obj);
				if (chain.Contains(val)) continue;
				hash[feild.Name] = IsLiteral(val) ? val : ConvertToHashTable(val, chain);
			}
			var props = type.GetProperties();
			foreach (var prop in props)
			{
				if (!prop.CanRead) continue;
				if (!IsEmpty(prop.CustomAttributes)) continue;
				val = prop.GetValue(obj);
				if (chain.Contains(val)) continue;
				hash[prop.Name] = IsLiteral(val) ? val : ConvertToHashTable(val, chain);
			}
			val = null;
			return hash;
		}

		public static string Convert(object value)
		{
			if (IsLiteral(value))
			{
				return ConvertLiteral(value);
			}
			else
			{
				if (value is IDictionary)
				{
					return ConvertObject(value as IDictionary);
				}
				else if (value is IEnumerable)
				{
					return ConvertArray(value as IEnumerable);
				}
				else
				{
					ArrayList chain = new ArrayList();
					return Convert(ConvertToHashTable(value, chain));
				}
			}
		}

	}
}